#ifndef __LOSU_NET_SK_H__
#define __LOSU_NET_SK_H__

#if defined _WIN64 || defined _WIN32
    #include <winsock2.h>
    #include <ws2tcpip.h> //包含inet_ntoa
    #include <windows.h>
#endif

#ifdef __linux__
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
#endif

#include <cstdio>
#include <cstdint>
#include <cstdlib>
#include <string>
#include <vector>

namespace net {
    using std::string;
    using std::vector;

#ifdef _WIN32
    typedef SOCKET socket_t;
#endif

    /// @brief 
    namespace sk {
        class Socket {
        private:
            socket_t sp;
        public:
            /* C AND DC */

            /// @brief 从已有的SOCKET创建对象
            /// @param s 已经创建的SOCKET指针
            Socket(socket_t s) { setPtr(s); }

            /// @brief 创建一个Socket对象
            /// @param family 地址族
            /// @param type 类型
            /// @param protocol 协议，可直接设置为0，函数会自动设置成正确的值
            Socket(int family = AF_INET, int type = SOCK_STREAM, int protocol = IPPROTO_TCP);

            /// @brief Socket的析构函数，会释放SOCKET资源
            ~Socket();

            /* Act */

            /// @brief TCP编程所用的连接函数
            /// @param ip 目标IP地址
            /// @param port 目标端口
            /// @return 是否成功连接
            bool connect(string ip, int port);

            /// @brief 将套接字绑定到对应的IP和端口
            /// @param ip 要绑定的IP
            /// @param port 要绑定的端口
            /// @return 是否成功绑定
            bool bind(string ip, int port = 80);

            /// @brief 设置TCP套接字所监听的最大值
            /// @param n 最大值
            /// @return 是否成功监听
            bool listen(int n = 5);

            /// @brief 接收客户端套接字
            /// @return 接收到的套接字
            Socket accept();

            /// @brief 发送信息
            /// @param data 要发送的数据
            /// @param l 要发送的数据的字节数
            /// @return 实际发送的字节数
            size_t send(const char *data, int l);

            /// @brief 发送信息
            /// @param s 要发送的数据
            /// @return 实际发送的字节数
            size_t send(string & s);

            /// @brief 发送信息
            /// @param v 要发送的数据
            /// @return 实际发送的字节数
            size_t send(vector<char> & v);

            /// @brief 发送信息
            /// @param ip 要发送到的IP
            /// @param port 要发送到的端口
            /// @param data 要发送的数据
            /// @param l 要发送的数据的字节数
            /// @return 发送的实际字节数
            size_t sendto(string ip, int port, const char *data, int l);

            /// @brief 发送信息
            /// @param ip 要发送到的IP
            /// @param port 要发送到的端口
            /// @param s 要发送的字符串
            /// @return 发送的实际字节数
            size_t sendto(string ip, int port, string & s);

            /// @brief 发送信息
            /// @param ip 要发送到的IP
            /// @param port 要发送到的端口
            /// @param v 要发送的数据
            /// @return 发送的实际字节数
            size_t sendto(string ip, int port, vector<char> & v);

            /// @brief 接收数据
            /// @param buff 接受数据的缓冲区
            /// @param l 要接受的数据的字节数
            /// @return 实际接受的字节数
            int recv(char *buff, size_t l);

            /// @brief 接收数据
            /// @param result 接收数据的vector
            /// @param l 要接受的数据的字节数
            /// @return 实际接受的字节数
            int recv(vector<char> & result, size_t l = 512);

            /// @brief 接收数据
            /// @param result 接收数据的string
            /// @param l 要接受的数据的字节数
            /// @return 实际接受的字节数
            int recv(string & result, size_t l = 512);

            /// @brief 接收全部数据
            /// @param result 接收数据的vector
            /// @return 实际接受的字节数
            int recvAll(vector<char> & result);

            /// @brief 接收全部数据
            /// @param result 接收数据的string
            /// @return 实际接受的字节数
            int recvAll(string & result);

            /// @brief 接受数据
            /// @param ip 数据的来源IP
            /// @param port 数据的来源端口
            /// @param buff 储存数据的缓冲区
            /// @param l 要接受的字节数
            /// @return 实际接受的字节数
            int recvFrom(string & ip, int & port, char *buff, size_t l = 512);

            /// @brief 接受数据
            /// @param ip 数据的来源IP
            /// @param port 数据的来源端口
            /// @param s 储存数据的string
            /// @param l 要接受的字节数
            /// @return 实际接受的字节数
            int recvFrom(string & ip, int & port, string & s, size_t l = 512);

            /// @brief 接受数据
            /// @param ip 数据的来源IP
            /// @param port 数据的来源端口
            /// @param v 储存数据的vector
            /// @param l 要接受的字节数
            /// @return 实际接受的字节数
            int recvFrom(string & ip, int & port, vector<char> & v, size_t l = 512);

            /* INFO GET */

            /// @brief 判断保存的套接字是否是可用的套接字
            /// @return 判断结果
            bool isUsable();

            /// @brief 获取当前套接字的地址族
            /// @return 
            int getFamily();

            /// @brief 获取当前套接字的类型
            /// @return 
            int getType();

            /// @brief 获取当前套接字的协议
            /// @return 
            int getProtocol();

            // int getDataLength();

            /// @brief 获取内部储存的SOCKET
            /// @return 获取的指针
            socket_t getPtr() { return sp; };

            /* DATA SET */

            /// @brief 设置套接字指针
            /// @param sock 要设置的指针
            void setPtr(socket_t sock) { sp = sock; };
        };

        /// @brief 快速创建一个TCP套接字
        /// @return 创建完毕的Socket
        Socket newTCPSocket();

        /// @brief 快速创建一个TCP客户端套接字
        /// @param sip 要连接到的IP
        /// @param sport 要连接到的端口
        /// @return 创建的Socket 
        Socket newTCPClient(string sip, int sport);

        /// @brief 快速创建一个TCP端套接字
        /// @param sip 要绑定的IP
        /// @param sport 要绑定的端口
        /// @return 创建的Socket 
        Socket newTCPSever(string sip, int sport, int la = 5);

        /// @brief 快速创建一个UDP套接字
        /// @return 创建完毕的Socket
        Socket newUDPSocket();
    }
}

#endif
